//   Copyright 2012,2013 Vaughn Vernon
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package com.saasovation.collaboration.port.adapter.persistence.view;

import com.saasovation.collaboration.domain.model.forum.DiscussionClosed;
import com.saasovation.collaboration.domain.model.forum.DiscussionReopened;
import com.saasovation.collaboration.domain.model.forum.DiscussionStarted;
import com.saasovation.common.event.sourcing.DispatchableDomainEvent;
import com.saasovation.common.event.sourcing.EventDispatcher;
import com.saasovation.common.port.adapter.persistence.AbstractProjection;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import java.util.List;
import java.util.Map;

public class MySQLDiscussionProjection extends AbstractProjection
        implements EventDispatcher {

    private final NamedParameterJdbcTemplate jdbcTemplate;

    private static final Class<?>[] understoodEventTypes = {
            DiscussionClosed.class,
            DiscussionReopened.class,
            DiscussionStarted.class
    };

    public MySQLDiscussionProjection(EventDispatcher aParentEventDispatcher,
                                     NamedParameterJdbcTemplate jdbcTemplate) {
        super();
        this.jdbcTemplate = jdbcTemplate;
        aParentEventDispatcher.registerEventDispatcher(this);
    }

    @Override
    public void dispatch(DispatchableDomainEvent aDispatchableDomainEvent) {
        this.projectWhen(aDispatchableDomainEvent);
    }

    @Override
    public void registerEventDispatcher(EventDispatcher anEventDispatcher) {
        throw new UnsupportedOperationException("Cannot register additional dispatchers.");
    }

    @Override
    public boolean understands(DispatchableDomainEvent aDispatchableDomainEvent) {
        return this.understandsAnyOf(
                aDispatchableDomainEvent.domainEvent().getClass(),
                understoodEventTypes);
    }

    protected void when(DiscussionClosed anEvent) {
        String sql = "update tbl_vw_discussion " +
                "set closed = 1 " +
                "where tenant_id = :tenantId " +
                "and discussion_id = :discussionId";
        MapSqlParameterSource params = new MapSqlParameterSource()
                .addValue("tenantId", anEvent.tenant().id())
                .addValue("discussionId", anEvent.discussionId().id());
        jdbcTemplate.update(sql, params);
    }

    protected void when(DiscussionReopened anEvent) {
        String sql = "update tbl_vw_discussion " +
                "set closed = 0 " +
                "where tenant_id = :tenantId " +
                "and discussion_id = :discussionId";
        MapSqlParameterSource params = new MapSqlParameterSource()
                .addValue("tenantId", anEvent.tenant().id())
                .addValue("discussionId", anEvent.discussionId().id());
        jdbcTemplate.update(sql, params);
    }

    protected void when(DiscussionStarted anEvent) {
        String existsSql = "select discussion_id from tbl_vw_discussion " +
                "where tenant_id = :tenantId " +
                "and discussion_id = :discussionId";
        MapSqlParameterSource existsParams = new MapSqlParameterSource()
                .addValue("tenantId", anEvent.tenant().id())
                .addValue("discussionId", anEvent.discussionId().id());

        List<Map<String, Object>> result = jdbcTemplate.queryForList(existsSql, existsParams);
        // idempotent operation
        if (!result.isEmpty()) {
            return;
        }
        String insertSql = "insert into tbl_vw_discussion( " +
                "discussion_id, author_email_address, author_identity, author_name, " +
                "closed, exclusive_owner, forum_id, " +
                "subject, tenant_id" +
                ") values(" +
                ":discussionId, :authorEmail, :authorIdentity, :authorName, " +
                ":closed, :exclusiveOwner, :forumId, " +
                ":subject, :tenantId)";
        MapSqlParameterSource insertParams = new MapSqlParameterSource()
                .addValue("discussionId", anEvent.discussionId().id())
                .addValue("authorEmail", anEvent.author().emailAddress())
                .addValue("authorIdentity", anEvent.author().identity())
                .addValue("authorName", anEvent.author().name())
                .addValue("closed", 0)
                .addValue("exclusiveOwner", anEvent.exclusiveOwner())
                .addValue("forumId", anEvent.forumId().id())
                .addValue("subject", anEvent.subject())
                .addValue("tenantId", anEvent.tenant().id());
        jdbcTemplate.update(insertSql, insertParams);
    }
}
